////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Roman Ranzmaier (http://www.ranzmaier.at/contact.aspx).  All rights reserved.
// 
// Codeplex project: http://gadgeteernetworking.codeplex.com
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

using System;
using Microsoft.SPOT;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using Ranzmaier.NETMF.IO;

namespace Ranzmaier.NETMF.GadgeteerNetworking
{

    /// <summary>
    /// Fast and Easy TCPServer
    /// </summary>
    public class TcpServer : NetWorkServerBase
    {

        private int _numOfListener;
        private Thread _listenerThread;
        /// <summary>
        /// Initializes a new instance of the <see cref="TcpServer"/> class.
        /// </summary>
        /// <param name="adr">The IP adress</param>
        /// <param name="port">The port.</param>
        /// <param name="numOfListener">The num of listener. (currently not in use)</param>
        /// <param name="timeout">The timeout.</param>
        public TcpServer(IPAddress adr, int port, int numOfListener, int timeout)
            : base(NetworkServerProtocol.TCP, adr, port, timeout)
        {
            _numOfListener = numOfListener;
            CreateSocket();
            _socket.Bind(new IPEndPoint(adr, Port));

        }


        #region Events
        #region BeginDataReceiveEvent
        /// <summary>
        /// DataReceiveEventArgs
        /// </summary>
        public class DataReceiveEventArgs : IDisposable
        {
            /// <summary>
            /// Binaryreader for reading incoming data
            /// </summary>
            private BinaryReader _reader = null;
            public BinaryReader Reader
            {
                get
                {
                    if (_reader == null)
                        _reader = new BinaryReader(_stream);
                    return _reader;

                }
            }
            /// <summary>
            /// Binarywriter for writing answer to client
            /// </summary>
            private BinaryWriter _writer = null;
            public BinaryWriter Writer
            {
                get
                {
                    if (_writer == null)
                        _writer = new BinaryWriter(_stream);
                    return _writer;

                }
            }

            private System.Net.Sockets.Socket _socket;
            private NetworkStream _stream;
            /// <summary>
            /// Initializes a new instance of the <see cref="DataReceiveEventArgs"/> class.
            /// </summary>
            /// <param name="socket">The clientsocket.</param>
            public DataReceiveEventArgs(System.Net.Sockets.Socket socket)
            {
                _socket = socket;
                _stream = new NetworkStream(_socket, true);
            }

            
            private bool _disposed = false;
            /// <summary>
            /// Releases unmanaged and - optionally - managed resources
            /// </summary>
            public void Dispose()
            {
                if (_disposed) return;
                if (_reader != null)
                    _reader.Dispose();
                _reader = null;
                
                if (_writer != null)
                    _writer.Dispose();
                _writer = null;

                if (_stream != null)
                    _stream.Dispose();
                _stream = null;

                Socket socket = _socket;
                if (_socket != null)
                    socket.Close();
                _socket = null;
               
                _disposed = true;
            }
        }
        #endregion

        /// <summary>
        /// DataReceivedEventHandler
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Gadgeteer.Modules.Module.NetworkModule.TCPServer.DataReceiveEventArgs"/> instance containing the event data.</param>
        public delegate void DataReceivedEventHandler(System.Net.Sockets.Socket sender, DataReceiveEventArgs e);
        /// <summary>
        /// Occurs when data received over the network
        /// </summary>
        public event DataReceivedEventHandler DataReceived;

        /// <summary>
        /// Called when data received over the network.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Gadgeteer.Modules.Module.NetworkModule.TCPServer.DataReceiveEventArgs"/> instance containing the event data.</param>
        protected virtual void OnDataReceivedEvent(System.Net.Sockets.Socket sender, DataReceiveEventArgs e)
        {
            if (DataReceived != null)
                DataReceived(sender, e);
        }
        #endregion

        #region NetWorkServerBase implementation
        /// <summary>
        /// Starts this instance.
        /// </summary>
        public override void Start()
        {
            if (!Started)
            {
                _socket.Listen(_numOfListener);
                Started = true;
                _listenerThread = new Thread(new ThreadStart(ClientListener));
                _listenerThread.Start();
            }
        }
        /// <summary>
        /// Stops this instance.
        /// </summary>
        public override void Stop()
        {
            if (Started)
            {
                try
                {
                    _listenerThread.Abort();
                }
                catch (ThreadAbortException) { }

                _listenerThread = null;
                _socket.Close();
            }
        }
        #endregion


        private void ClientListener()
        {
            while (true)
            {
                var clientCon = _socket.Accept();
                var clientDataReader = new NetDataReader(clientCon);
                clientDataReader.DataReceived += (s, e) => OnDataReceivedEvent(s, e);
                Thread reader = new Thread(clientDataReader.DataReader);
                reader.Start();
            }
        }

        #region NetDataReader class
        /// <summary>
        /// Helper class for TCPServer
        /// </summary>
        internal class NetDataReader
        {
            private System.Net.Sockets.Socket _socket;
            /// <summary>
            /// Initializes a new instance of the <see cref="NetDataReader"/> class.
            /// </summary>
            /// <param name="socket">The socket.</param>
            public NetDataReader(System.Net.Sockets.Socket socket)
            {
                _socket = socket;
            }

            /// <summary>
            /// Used by TCPServer ClientListener to handle network stream access in own thread 
            /// 
            /// </summary>
            internal void DataReader()
            {
                OnDataReceive();
            }

            /// <summary>
            /// BeginDataReceiveEventHandler
            /// </summary>
            /// <param name="sender">The sender.</param>
            /// <param name="e">The <see cref="Gadgeteer.Modules.Module.NetworkModule.TCPServer.DataReceiveEventArgs"/> instance containing the event data.</param>
            internal delegate void BeginDataReceiveEventHandler(System.Net.Sockets.Socket sender, DataReceiveEventArgs e);
            /// <summary>
            /// Occurs when [data received].
            /// </summary>
            internal event BeginDataReceiveEventHandler DataReceived;
            protected virtual void OnDataReceive()
            {
                using (DataReceiveEventArgs e = new DataReceiveEventArgs(_socket))
                {
                    if (DataReceived != null)
                        DataReceived(_socket, e);

                }
            }
        }
        #endregion
    }

}
